#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>

// 孤儿进程
// 父进程先结束掉，子进程还没有结束
// 此时子进程会被1号进程领养
// 如果子进程不被领养，子进程结束之后进入僵尸进程之后，就没有人能够获取它的结束信息，该子进程也就不会结束了
// 子进程被领养之后，就会变成后台进程
// ./cmd+&
void orphanProcess()
{
    // 创建子进程
    pid_t id = fork();
    
    if(id == 0)
    {
        // child
        while(1)
        {
            printf("我是子进程，pid:%d, ppid:%d\n",getpid(), getppid());
            sleep(1);
        }
    }
    else
    {
        // father
        int count = 5;
        while(count)
        {
            printf("我是父进程，pid:%d, ppid:%d\n",getpid(), getppid());
            count--;
            sleep(1);
        }
    }
}

void testPriority()
{
    // 进程优先级
    // 进程优先级就是进程获取CPU资源的先后顺序，因为cpu的资源是有限的，而进程又比较多，所以需要借助优先级来解决谁先谁后的问题
    // 优先级是在task_struct中的一个数字，可以用ps -al来查看当前运行进程的优先级，PRI表示优先级，NI是nice值，进程优先级的修正数据
    // 如果想要修改优先级，不能直接修改RPI，而要通过修改nice值来改变
    // PRI的默认值是80，nice的默认值为0，nice的修改范围为[-20,19]，真正的PRI = PRI默认值+nice值，所以PRI的范围为[60,99]共40是个优先级
    // uid
    // 我们在查看优先级的时候除了pid和ppid，还会看到uid，ccid其实就是我们用户的识别码
    // 在权限部分，操作系统是怎么知道我们值拥有者，所属组，还是other的？
    // 我们访问文件的命令其实也是一个一个的进程，而进程会保存打开进程的用户的uid
    // 当我们访问文件的时候，就会将进程保存的uid和文件的uid进行比较(对于我们来说，我们看到的是用户名和所属组，但是对于操作系统来说，只能看到uid)
    while(1)
    {
        printf("testPriority(),pid:%d\n",getpid());
        sleep(1);
    }

    // 我们可以借助top命令，然后r，再设置nice值，就可以达到修改PRI的效果
    // 还可以借助nice命令，renice命令和一些系统调用来修改PRI优先级
    // 设立优先级时要合理，不合理的优先级设立可能导致低优先级的进程迟迟得不到cpu的资源，进而导致进程饥饿
}

// 竞争性：系统进程数众多，CPU资源只有少量，甚至1个，所以进程之间是具有竞争属性的。为了高效完成任务，更合理竞争相关资源，便具有了优先级
// 独立性：多进程运行，需要独享各种资源，多进程同时运行之间是互相独立的
// 并行：多个进程在多个cpu下同时进行
// 并发：多个进程在一个cpu下采取切换的方式，在一段时间内，使得每个进程都得以推进


// 切换
// 一个程序被cpu运行，不会一直将该进程跑完，而有一个时间片的东西，等到该进程跑完了时间片，就会被cpu剥离，让下一个进程运行
// 因为这个的存在，所以如果一个程序进入死循环，操作系统并不会卡死，而是等到时间片到了，就切换下一个进程运行
// 当运行进程a时，操作系统会将进程a的信息写入cpu的多个寄存器中，然后等到时间片到了，操作系统会将进程a在寄存器中的临时信息拷贝一份放到tast_struct中
// 的TSS，接着让下一个进程b将其上下文资源放入寄存器中。等到又一次运行到进程a时，此时再将其第一次运行的信息放入寄存器中，此时就会接着地第一次的地方开始运行，而不会从头开始
// 切换最核心的就是，保存和恢复当前进程的硬件上下文的数据，即cpu中寄存器里面的数据


// linux中真正的调度算法：O(1)调度算法
// runqueue中有两个类似于哈希表的结构queue存在，里面保存的就是一个一个的tast_struct*，优先级相同的保存在同一个下标位置，即每一个下标位置都是一个队列
// 该哈希表的大小为140，前100个都是实时优先级，不需要考虑，下标从100开始的40个优先级是我们所要考虑的，即分时优先级
// 进行进程切换的时候，就得遍历哈希表来找到优先级靠前的进程，但是这样还是得遍历，效率低
// 所以还有一个bitmap[5]，它是一个位图，它是unsigned int 类型，他用5个成员的32个比特位，描述了这140个队列中是否有进程，1/0，有/没有
// nr_active则表示的是当前这个queue有多少个进程

// 但是上面的设计还是有点问题，当我们一个优先级比较高的时间片运行结束后，它又会插入到queue中原优先级的位置处，这个位置一直有进程
// 就会导致优先级比较低的进程一直不会被执行到，就会导致饥饿进程
// 为了解决这个问题，所以用一个reque_elem将上面三个数据包含了，在runqueue中，加入两个reque_elem，此时就有了两个大小为140的queue队列存在
// 而这两个reque_elem其实是包含在reque_elem_prio_arrary[2]这个数组中，0表示活跃进程，1表示过期进程
// 当一个进程被执行了一次之后，他就会插入到过期进程的队列中
// cpu会将活跃进程里面的所有进程运行一遍之后，让其为空，然后在遍历过期进程，但是这样就会导致原先0下标为活跃1下标为过期的性质颠倒了
// 所以在runqueue中加入了两个指针，active和expired，分别执行活跃和过期，当活跃为空后，交换这两个指针的内容，此时active就又指向了活跃

int main()
{
    testPriority();
    // orphanProcess();
    return 0;
}
